package com.riteny.token.helper.resolver;

import com.riteny.token.helper.anno.ClientAccessTokenResolverAnno;
import com.riteny.token.helper.entity.ClientTokenInformation;
import com.riteny.token.helper.exception.TokenVaildException;
import com.riteny.token.helper.parse.HeaderTokenParser;
import com.riteny.token.helper.parse.HeaderTokenParserImpl;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Service
public class CilentAccessTokenArgumentTypeResolver implements HandlerMethodArgumentResolver {

    private RestTemplate restTemplate = new RestTemplate();

    @Value("${token.helper.url}")
    private String tokenUrl;

    @Autowired(required = false)
    private HeaderTokenParser headerTokenParser;

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(ClientAccessTokenResolverAnno.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {

        ClientAccessTokenResolverAnno annotation = methodParameter.getParameterAnnotation(ClientAccessTokenResolverAnno.class);

        HttpServletRequest httpServletRequest = (HttpServletRequest) nativeWebRequest.getNativeRequest();

        //step1 确定解析器
        headerTokenParser = headerTokenParser == null ? new HeaderTokenParserImpl() : headerTokenParser;

        //step2 解析授权令牌
        String token = headerTokenParser.parese(httpServletRequest.getHeader("Authorization"));

        //step3 获取授权令牌对应的用户资料
        JSONObject response = null;

        try {
            response = restTemplate.getForEntity(tokenUrl + "/oauth/check_token?token=" + token, JSONObject.class).getBody();
        } catch (HttpClientErrorException e) {
            JSONObject errorMsg = JSONObject.fromObject(e.getResponseBodyAsString());
            throw new TokenVaildException(errorMsg.optString("error"), errorMsg.optString("error_description"));
        }

        ClientTokenInformation clientTokenInformation = new ClientTokenInformation(response);

        checkScopes(clientTokenInformation.getScopes(), annotation.scopes());

        return clientTokenInformation;
    }

    private void checkScopes(List<String> userScopes, String[] needScopes) throws TokenVaildException {
        for (String needAuthority : needScopes) {
            if (!userScopes.contains(needAuthority)) {
                //假如需要的权限并不包含在用户权限内，抛出权限不足异常
                throw new TokenVaildException("invalid_token", "Insufficient scope " + needAuthority);
            }
        }
    }
}
